import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.util.Base64Utils;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;

/**
 * 微信支付工具类
 */
@Slf4j
public class WeChatPayUtil {
    /**
     * 生成签名
     *
     * @param signatureStr   签名字符串
     * @param privateKeyPath 私钥
     * @return 签名结果
     */
    public static String getSign(String signatureStr, String privateKeyPath) {
        try {
            // 替换私钥中的转义字符
            String replace = privateKeyPath.replace("\\n", "\n");
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(Files.newInputStream(Paths.get(replace)));

            // 初始化签名对象
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(merchantPrivateKey);

            // 更新签名数据
            if (StrUtil.isNotBlank(signatureStr)) {
                sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
            }

            // 返回签名结果
            return Base64Utils.encodeToString(sign.sign());
        } catch (InvalidKeyException |
                 NoSuchAlgorithmException |
                 SignatureException |
                 IOException e) {
            log.error("签名生成失败: {}", e.getMessage(), e);
            throw new ServiceException("签名生成失败 原因" + e.getMessage());
        }
    }

    /**
     * 获取随机字符串 Nonce Str
     *
     * @return 随机字符串
     * <p>
     * 注意事项：
     * 1. 使用 SecureRandom 提高随机性，适用于安全性要求较高的场景。
     * 2. 对 SYMBOLS 进行校验，避免空指针或越界异常。
     */
    public static String generateNonceStr() {
        // 定义可用字符集
        final String SYMBOLS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

        // 初始化随机数生成器，使用 SecureRandom 提高随机性
        SecureRandom random = new SecureRandom();
        char[] nonceChars = new char[32];

        for (int i = 0; i < nonceChars.length; i++) {
            // 确保随机索引在合法范围内
            nonceChars[i] = SYMBOLS.charAt(random.nextInt(SYMBOLS.length()));
        }

        return new String(nonceChars);
    }

    /**
     * 获取商户私钥
     *
     * @param privateKeyPath 私钥存放地址
     * @return 私钥
     */
    private static PrivateKey getPrivateKey(String privateKeyPath) {

        //获取商户私钥
        try (FileInputStream fis = new FileInputStream(privateKeyPath)) {
            return PemUtil.loadPrivateKey(fis);
            // 使用私钥进行操作
        } catch (IOException e) {
            throw new ServiceException("私钥文件不存在" + e);
        }
    }


    /**
     * 获取证书管理器实例
     *
     * @param privateKey  私钥
     * @param mchId       商户号
     * @param mchSerialNo 商户API证书序列号
     * @param apiV3Key    APIv3密钥
     * @return 证书管理器实例
     */
    private static Verifier getVerifier(PrivateKey privateKey,
                                        String mchId,
                                        String mchSerialNo,
                                        String apiV3Key) throws IOException, NotFoundException {

        log.info("================加载证书管理器实例");
        // 获取证书管理器单例实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();

        try {
            // 该方法底层已实现同步线程更新证书
            // 详见beginScheduleUpdate()方法
            certificatesManager.putMerchant(mchId,
                    new WechatPay2Credentials(mchId,
                            new PrivateKeySigner(mchSerialNo, privateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8));
        } catch (GeneralSecurityException | HttpCodeException e) {
            log.error("商户证书加载失败: {}", e.getMessage(), e);
        }
        log.info("================证书管理器实例加载完成");

        log.info("================证书管理器中获取验签器");
        Verifier verifier = certificatesManager.getVerifier(mchId);
        log.info("================获取验签器成功");
        return verifier;
    }

    /**
     * 获取微信支付客户端
     *
     * @param mchId          商户号
     * @param mchSerialNo    商户API证书序列号
     * @param apiV3Key       APIv3密钥
     * @param privateKeyPath 私钥文件存放地址
     * @return 微信支付客户端
     */
    public static CloseableHttpClient getWxPayClient(String mchId,
                                                     String mchSerialNo,
                                                     String apiV3Key,
                                                     String privateKeyPath) throws NotFoundException, IOException {

        log.info("================开始构造httpClient================");
        //获取私钥
        PrivateKey getPrivateKey = getPrivateKey(privateKeyPath);
        //加载证书实例
        Verifier verifier = getVerifier(getPrivateKey, mchId, mchSerialNo, apiV3Key);

        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, mchSerialNo, getPrivateKey)
                .withValidator(new WechatPay2Validator(verifier));

        // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签，并进行证书自动更新
        CloseableHttpClient httpClient = builder.build();
        log.info("================构造httpClient成功================");
        return httpClient;
    }

    public static String verifySign(SepWxConfigDTO dto, HttpServletRequest request) throws NotFoundException, IOException {
        //检查header
        //获取报文
        String data = getRequestBody(request);

        //随机串
        String nonce = request.getHeader("Wechatpay-Nonce");

        //微信传递过来的签名
        String signature = request.getHeader("Wechatpay-Signature");

        //证书序列号（微信平台）
        String serial = request.getHeader("Wechatpay-Serial");
        //时间戳
        String timestamp = request.getHeader("Wechatpay-Timestamp");

        String message = timestamp + "\n" + nonce + "\n" + data + "\n";
        //获取私钥
        PrivateKey getPrivateKey = getPrivateKey(dto.getPrivateKeyPath());
        //加载证书实例
        Verifier verifier = getVerifier(getPrivateKey, dto.getMchId(), dto.getMchSerialNo(), dto.getApiV3Key());

        if (!verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature)) {
            log.info("==============签名校验失败");
            return null;
        }
        return data;
    }

    private static String getRequestBody(HttpServletRequest request) {

        StringBuilder sb = new StringBuilder();

        try (ServletInputStream inputStream = request.getInputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
            String line;

            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException ignored) {
        }
        return sb.toString();
    }

    /**
     * 发送get请求
     *
     * @param mchId          商户号
     * @param mchSerialNo    微信商户号
     * @param apiV3Key       微信v3秘钥
     * @param privateKeyPath 私钥路径
     * @param body           请求参数
     * @param url            请求路径
     * @return 微信响应
     */
    public static JSONObject sendGetWeChatPay(String mchId,
                                              String mchSerialNo,
                                              String apiV3Key,
                                              String privateKeyPath,
                                              String body,
                                              String url) throws NotFoundException, IOException {
        HttpGet request = new HttpGet(url + body + "?mchid=" + mchId);
        request.addHeader("Accept", "application/json");
        request.addHeader("Content-type", "application/json");
        CloseableHttpClient client = getWxPayClient(mchId, mchSerialNo, apiV3Key, privateKeyPath);
        CloseableHttpResponse response = client.execute(request);
        String string = EntityUtils.toString(response.getEntity());
        return JSONObject.parseObject(string);
    }

    /**
     * 发送post请求
     *
     * @param mchId          商户号
     * @param mchSerialNo    微信商户号
     * @param apiV3Key       微信v3秘钥
     * @param privateKeyPath 私钥路径
     * @param body           请求参数(JSON格式)
     * @param url            请求路径
     * @return 微信响应
     */
    public static JSONObject sendPostWeChatPay(String mchId,
                                               String mchSerialNo,
                                               String apiV3Key,
                                               String privateKeyPath,
                                               String body,
                                               String url) throws NotFoundException, IOException {
        HttpPost request = new HttpPost(url);

        request.addHeader("Accept", "application/json");
        request.addHeader("Content-type", "application/json");
        request.setEntity(new StringEntity(body, "UTF-8"));
        CloseableHttpClient client = getWxPayClient(mchId, mchSerialNo, apiV3Key, privateKeyPath);
        CloseableHttpResponse response = client.execute(request);
        String string = EntityUtils.toString(response.getEntity());
        return JSONObject.parseObject(string);
    }

}
